package com.uade.empresasdistribuidas.dao;

import java.io.Serializable;
import java.util.List;
import java.lang.reflect.ParameterizedType;
import org.hibernate.Criteria;
import org.hibernate.LockMode;
import org.hibernate.Session;
import org.hibernate.criterion.Criterion;
import org.hibernate.criterion.Example;


public abstract class GenericHibernateDAO<T, K extends Serializable>  
        implements GenericDAO<T, K> {  
  
    private Class<T> persistentClass;  
    private Session session;
  
    public GenericHibernateDAO() {  
        this.persistentClass = (Class<T>) getParameterClass(getClass(), 0);
     }    
  
    @SuppressWarnings("unchecked")  
    public void setSession(Session s) {  
        this.session = s;  
    }  
  
    protected Session getSession() {  
        if (session == null)  
            throw new IllegalStateException("Session has not been set on DAO before usage");  
        return session;  
    }  
  
    public Class<T> getPersistentClass() {  
        return persistentClass;  
    }  
  
    @SuppressWarnings("unchecked")  
    public T findByIdByLock(K id, boolean lock) {  
        T entity;  
        if (lock)  
            entity = (T) getSession().load(getPersistentClass(), id, LockMode.UPGRADE);  
        else  
            entity = (T) getSession().load(getPersistentClass(), id);  
  
        return entity;  
    }  
    
    @SuppressWarnings("unchecked")  
	public T findById(K id) {
		T entity;
		entity = (T) getSession().load(getPersistentClass(), id);
		return entity;
	}
  
    @SuppressWarnings("unchecked")  
    public List<T> findAll() {  
        return findByCriteria();  
    }  
  
    @SuppressWarnings("unchecked")  
    public List<T> findByExample(T exampleInstance, String[] excludeProperty) {  
        Criteria crit = getSession().createCriteria(getPersistentClass());  
        Example example =  Example.create(exampleInstance);  
        for (String exclude : excludeProperty) {  
            example.excludeProperty(exclude);  
        }  
        crit.add(example);  
        return crit.list();  
    }  
  
    @SuppressWarnings("unchecked")  
    public T makePersistent(T entity) { 
    	getSession().beginTransaction();
        getSession().saveOrUpdate(entity);
        getSession().getTransaction().commit();
        return entity;  
    }  
  
    public void makeTransient(T entity) {  
    	getSession().beginTransaction();
        getSession().delete(entity);  
        getSession().getTransaction().commit();
    }  
  
    public void flush() {  
        getSession().flush();  
    }  
  
    public void clear() {  
        getSession().clear();  
    }  
  
    /** 
     * Use this inside subclasses as a convenience method. 
     */  
    @SuppressWarnings("unchecked")  
    protected List<T> findByCriteria(Criterion... criterion) {  
        Criteria crit = getSession().createCriteria(getPersistentClass());  
        for (Criterion c : criterion) {  
            crit.add(c);  
        }  
        return crit.list();  
   }  
  
   private static Class<?> getParameterClass(Class<?> clazz, int index) {
		return (Class<?>) (((ParameterizedType) clazz.getGenericSuperclass())
				.getActualTypeArguments()[index]);
	}

   @Override
   public List<T> findByExample(T exampleInstance) {
	   Criteria crit = getSession().createCriteria(getPersistentClass());  
       Example example =  Example.create(exampleInstance);  
       crit.add(example);  
       return crit.list();  
   }
}  